/**
 * 替换数组中指定索引的元素
 * @param arr
 * @param index
 * @param newValue
 */
export function replaceElAtIndex<T>(arr: T[], index: number, newValue: T): T[] {
  return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
}

/**
 * 在数组指定位置插入元素
 * @param arr
 * @param index
 * @param insertValue
 */
export function insertElAtIndex<T>(arr: T[], index: number, insertValue: T): T[] {
  return [...arr.slice(0, index), insertValue, ...arr.slice(index)];
}

/**
 * 删除数组指定索引的元素
 * @param arr
 * @param index
 */
export function removeElAtIndex<T>(arr: T[], index: number): T[] {
  return [...arr.slice(0, index), ...arr.slice(index + 1)];
}

/**
 * 交换数组中两个索引对应的元素,返回原数组对象
 * @param arr
 * @param a 索引A
 * @param b 索引B
 */
export function swapIndex<T>(arr: T[], a: number, b: number): T[] {
  const temp = arr[a];
  arr[a] = arr[b];
  arr[b] = temp;
  return arr;
}

/**
 * 将索引元素移动到指定位置
 * @param arr
 * @param index
 * @param targetIndex
 */
export function moveIndex<T>(arr: T[], index: number, targetIndex: number): T[] {
  if (index === targetIndex) {
    return arr;
  }
  const moveEl = arr[index];
  arr.splice(index, 1);
  return insertElAtIndex(arr, targetIndex, moveEl);
}

export interface RemoveElOptions {
  /** 当删除一个后是否停止继续查找 */
  stopOnRemoveOne?: boolean;
  /** 返回新的数组 */
  returnNewArray?: boolean;
}

/**
 * 删除数组指定元素
 * @param array 数组
 * @param el 指定元素
 * @param options {@link RemoveElOptions}
 */
export function removeEl<T>(array: T[], el: T, options?: RemoveElOptions): T[];
export function removeEl<T>(array: T[], predicate: (item: T, index: number, array: T[]) => boolean, options?: RemoveElOptions): T[];

export function removeEl<T>(array: T[], el: T | ((item: T, index: number, array: T[]) => boolean), options: RemoveElOptions = {}): T[] {
  const { stopOnRemoveOne = true, returnNewArray = true } = options;
  const retArr = returnNewArray ? array.slice() : array;
  const isFunc = typeof el === "function";
  for (let i = array.length - 1; i >= 0; i--) {
    if (isFunc) {
      if ((el as Function)(array[i], i, array)) {
        retArr.splice(i, 1);
        if (stopOnRemoveOne) {
          break;
        }
      }
    } else {
      if (array[i] === el) {
        retArr.splice(i, 1);
        if (stopOnRemoveOne) {
          break;
        }
      }
    }
  }
  return retArr;
}

/**
 * 从数组中删除或增加元素, 存在则删除,不存在则添加, 并返回新的数组
 * @param array 数组
 * @param item 删除/增加的元素
 * @param predicate 查找函数
 */
export function removeOrPushEl<T>(array: T[], item: T, predicate?: (item: T, index: number, array: T[]) => boolean): T[] {
  const index = array.findIndex(predicate || (it => it === item));
  return index !== -1 ? removeElAtIndex(array, index) : [...array, item];
}

/**
 * 创建一个数组
 * @param length 数组长度
 * @param fill 生成数组元素
 */
export function newArray<T>(length: number, fill: (i: number, array: T[]) => T): T[] {
  const array: T[] = [];
  for (let i = 0; i < length; i++) {
    array.push(fill(i, array));
  }
  return array;
}

export function reduce<T, R = T[]>(array: T[], reduceFn: (prev: R, current: T, index: number, array: T[]) => R, initValue: R): R {
  return array.reduce(reduceFn, initValue);
}

/**
 * 合并两个数组
 * @param arr1
 * @param arr2
 * @param predicate 去重判断
 */
export function mergeArray<T>(arr1: T[], arr2: T[], predicate?: (result: T[], item2: T, index2: number, arr2: T[]) => unknown): T[] {
  const newArr: T[] = arr1.slice();
  for (let i = 0; i < arr2.length; i++) {
    const val = arr2[i];
    if (predicate) {
      if (predicate(newArr, val, i, arr2)) {
        newArr.push(val);
      }
    } else {
      if (newArr.findIndex(it => it === val) === -1) {
        newArr.push(val);
      }
    }
  }
  return newArr;
}

/**
 * 将数组转换成对象
 * @param array 数组
 * @param key 数组中元素的key或者一个函数
 * @return 返回以key参数值为Key,数组元素对象为Value的对象
 */
export function array2object<T, K extends keyof T>(array: T[], key: K | ((it: T) => string)): Record<string, T>;

/**
 * 将数组转换成对象
 * @param array 数组
 * @param key 数组中元素的key或者一个函数
 * @param value 提供value的函数
 * @return 返回以key参数值为Key,value参数返回值为Value的对象
 */
export function array2object<T, R, K extends keyof T>(array: T[], key: K | ((it: T) => string), value: (it: T) => R): Record<string, R>;

/**
 * 将数组转换成对象
 * @param array 数组
 * @param key 数组中元素的key
 * @param value
 * @return 返回以key参数值为Key,数组元素对象为Value的对象
 */
export function array2object<T, K extends keyof T, R = any>(array: T[], key: K | ((it: T) => string), value?: (it: T) => R) {
  const obj = Object.create(null);
  const keyIsFunc = typeof key === "function";
  const valueIsFunc = typeof value === "function";
  for (const it of array) {
    const newKey = keyIsFunc ? key(it) : it[key];
    obj[newKey] = valueIsFunc ? value(it) : it;
  }
  return obj;
}

interface Array2TreeOptions<T> {
  /**
   * ID属性名称
   * @default 'id'
   */
  idProp?: keyof T;
  /**
   * 父级ID属性名称
   * @default 'parentId'
   */
  parentIdProp?: keyof T;
  /**
   * 顶级parentId的值
   */
  parentIdVal?: any;
  /**
   * 子列表属性名称
   * @default 'children'
   */
  childrenProp?: keyof T;
  /**
   * 如果children长度为0,删除children属性
   * @default false
   */
  trimEmptyChildren?: boolean;
}

/**
 * 数组转树  非递归求解
 * 利用数组和对象相互引用  时间复杂度O(n)
 * @param array
 * @param opts
 */
export function array2tree<T extends object>(array: T[], opts?: Array2TreeOptions<T>): T[] {
  const {
    idProp = "id" as keyof T,
    parentIdProp = "parentId" as keyof T,
    parentIdVal,
    childrenProp = "children" as keyof T,
  } = (opts ?? Object.create(null)) as Array2TreeOptions<T>;

  const retArray: T[] = [];
  const childrensMap: Record<string, T[]> = Object.create(null);
  const subChildren: T[] = [];

  // 全部遍历一遍分出最顶层的数据和非顶层数据
  for (const item of array) {
    const id = item[idProp] as unknown as string;
    const children: T[] = [];
    childrensMap[id] = children;
    item[childrenProp] = children as any;

    // 最顶层的
    if (item[parentIdProp] == null || item[parentIdProp] === "" || item[parentIdProp] === parentIdVal) {
      retArray.push(item);
    } else {
      // 子列表数据
      subChildren.push(item);
    }
  }

  // 再遍历一遍子数据列表, 将它们放入对应的父级children列表中
  for (const subChild of subChildren) {
    const pid = subChild[parentIdProp] as unknown as string;
    if (childrensMap[pid]) {
      childrensMap[pid].push(subChild);
    } else {
      // 父级不存在，放入顶层
      retArray.push(subChild);
    }
  }

  if (opts?.trimEmptyChildren) {
    for (const it of array) {
      if ((it[childrenProp] as any).length === 0) {
        delete it[childrenProp];
      }
    }
  }

  return retArray;
}

export type TreeDataLike<T> = T & { children?: T[] };

/**
 * 映射树形数组
 * @param array 树形数组
 * @param func 映射函数, 四个参数为:(数组元素,索引,原数组,当前元素的父元素)
 */
export function mapTreeData<T, R>(
  array: TreeDataLike<T>[],
  func: (it: TreeDataLike<T>, i: number, array: TreeDataLike<T>[], parent?: R) => TreeDataLike<R>,
): TreeDataLike<R>[] {
  return mapTreeData0(array, func);
}

function mapTreeData0<T, R>(
  array: TreeDataLike<T>[],
  func: (it: TreeDataLike<T>, i: number, array: TreeDataLike<T>[], parent?: R) => TreeDataLike<R>,
  parent?: R | undefined,
): TreeDataLike<R>[] {
  const ret: TreeDataLike<R>[] = [];
  const length = array.length;
  for (let i = 0; i < length; i++) {
    const originItem = array[i];
    const item = func(originItem, i, array, parent);
    if (originItem.children && originItem.children.length > 0) {
      item.children = mapTreeData0(originItem.children as TreeDataLike<T>[], func, item);
    }
    ret.push(item);
  }
  return ret;
}

/**
 * 铺平映射树形数组
 * @param array 树形数组
 * @param func 映射函数, 四个参数为: (数组元素,索引,原数组,当前元素的父元素)
 */
export function flatMapTreeData<T, R>(array: TreeDataLike<T>[], func: (it: T, i: number, array: T[], parent?: any) => R): R[] {
  return flatMapTreeData0(array, func);
}

function flatMapTreeData0<T, R>(array: TreeDataLike<T>[], func: (it: T, i: number, array: T[], parent?: any) => R, parent?: R): R[] {
  const ret: R[] = [];
  const length = array.length;
  for (let i = 0; i < length; i++) {
    const it = array[i];
    const newIt = func(it, i, array, parent);
    ret.push(newIt);
    if (it.children && it.children.length > 0) {
      ret.push(...flatMapTreeData0(it.children as TreeDataLike<T>[], func, newIt));
    }
  }
  return ret;
}

/**
 * 从树形结构数组中查找元素
 * @param array 树形数组
 * @param predicate 条件
 */
export function findTreeData<T>(
  array: TreeDataLike<T>[],
  predicate: (it: TreeDataLike<T>, i: number, array: TreeDataLike<T>[], parent?: TreeDataLike<T>) => unknown,
) {
  return findTreeData0(array, predicate, undefined);
}

function findTreeData0<T>(
  array: TreeDataLike<T>[],
  predicate: (it: TreeDataLike<T>, i: number, array: TreeDataLike<T>[], parent?: TreeDataLike<T>) => unknown,
  parent?: TreeDataLike<T>,
): TreeDataLike<T> | undefined {
  for (let i = 0; i < array.length; i++) {
    const item = array[i];
    if (!!predicate(item, i, array, parent)) {
      return item;
    }
    if (item.children != null && item.children.length > 0) {
      const subItem = findTreeData0(item.children as TreeDataLike<T>[], predicate, item);
      if (subItem != null) {
        return subItem;
      }
    }
  }
  return undefined;
}

export function foreachTreeData<T>(
  array: TreeDataLike<T>[],
  consumer: (it: TreeDataLike<T>, i: number, array: TreeDataLike<T>[], parent?: TreeDataLike<T>) => unknown,
) {
  foreachTreeData0(array, consumer, undefined);
}

function foreachTreeData0<T>(
  array: TreeDataLike<T>[],
  consumer: (it: TreeDataLike<T>, i: number, array: TreeDataLike<T>[], parent?: TreeDataLike<T>) => unknown,
  parent?: TreeDataLike<T>,
) {
  for (let i = 0; i < array.length; i++) {
    const it = array[i];
    consumer(it, i, array, parent);
    if (Array.isArray(it.children) && it.children.length > 0) {
      foreachTreeData0(it.children as TreeDataLike<T>[], consumer, it);
    }
  }
}

/**
 * 将一个数组降序排列
 * @param array 要排序的数组
 * @param field 对象数组中对象的字段名
 * @param parseValue 将值转换后再比较
 */
export function orderByDesc<T, K extends keyof T>(array: T[], field?: K, parseValue?: (value: T[K]) => any): T[] {
  if (!field) {
    return array.sort((a, b) => (a > b ? -1 : 0));
  }
  if (parseValue) {
    return array.sort((a, b) => {
      const av = parseValue(a[field]);
      const bv = parseValue(b[field]);
      return av > bv ? -1 : 0;
    });
  }
  return array.sort((a, b) => (a[field] > b[field] ? -1 : 0));
}

/**
 * 将数组升序排列
 * @param array 要排序的数组
 * @param field 对象数组中对象的字段名
 * @param parseValue 将值转换后再比较
 */
export function orderByAsc<T, K extends keyof T>(array: T[], field?: K, parseValue?: (value: T[K]) => any): T[] {
  if (!field) {
    return array.sort((a, b) => (b > a ? -1 : 0));
  }
  if (parseValue) {
    return array.sort((a, b) => {
      const av = parseValue(a[field]);
      const bv = parseValue(b[field]);
      return bv > av ? -1 : 0;
    });
  }
  return array.sort((a, b) => (b[field] > a[field] ? -1 : 0));
}

/**
 * 将一维数组拆分成二维数组
 * @param array 一维数组
 * @param lengthPer2d 二维数组内的每个数组的长度
 */
export function arrayTo2d<T>(array: T[], lengthPer2d: number): T[][] {
  const ret: T[][] = [];
  for (let i = 0; i < array.length; i += lengthPer2d) {
    ret.push(array.slice(i, i + lengthPer2d));
  }
  return ret;
}

/**
 * 数组求和
 * @param array
 */
export function sum(array: number[]): number {
  return array.reduce((p, c) => p + c, 0);
}

/**
 * 数组求平均值
 * @param array
 */
export function avg(array: number[]): number {
  return sum(array) / array.length;
}

/**
 * 返回数组的最后一个元素
 * @param array
 */
export function lastEl<T>(array: T[] | null | undefined): T | undefined {
  if (array == null || !Array.isArray(array) || array.length === 0) {
    return undefined;
  }
  return array[array.length - 1];
}

/**
 * 判断两个数组是否相同
 * @param a
 * @param b
 */
export function isSameArray(a: any[] | null | undefined, b: any[] | null | undefined): boolean {
  if (a == null || b == null || !Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
    return false;
  }

  for (let i = 0; i < a.length; i++) {
    const ai = a[i];
    const bi = b[i];
    if (Array.isArray(ai) && Array.isArray(bi) && !isSameArray(ai, bi)) {
      return false;
    }

    if (ai !== bi) {
      return false;
    }
  }

  return true;
}

/**
 * 将集合分组
 * @param array
 * @param field
 */
export function groupBy<T, K extends keyof T | string>(array: T[], field: K | ((item: T) => string)): Record<string, T[]> {
  const ret: Record<string, T[]> = Object.create(null);
  const keyIsString = typeof field === "string";
  for (let item of array) {
    const key = keyIsString ? (item as any)[field] : (field as Function)(item);
    if (!ret[key]) {
      ret[key] = [];
    }
    ret[key].push(item);
  }
  return ret;
}

export function findMax<T>(array: T[], func: (it: T, index: number, array: T[]) => number): T {
  let ret: T | undefined;
  let prevVal: number | undefined;
  for (let i = 0; i < array.length; i++) {
    const item = array[i];
    const value = func(item, i, array);
    if (prevVal == undefined || value > prevVal) {
      prevVal = value;
      ret = item;
    }
  }
  return ret!;
}

export function toArray<T>(v: T | T[] | null | undefined): T[] {
  if (v == null) {
    return [];
  }
  if (Array.isArray(v)) {
    return v;
  }
  if (typeof v === "string" && v === "") {
    return [];
  }
  return [v];
}

/**
 * 传入一个数组,过滤false/null/undefined值
 * @param arr
 */
export function arrayOf<T>(arr: (T | false | null | undefined)[]): T[] {
  return arr.filter(it => it != null && it !== false) as T[];
}
